package shader

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"learn-opengl/util"
	"strings"

	"github.com/go-gl/gl/v3.3-core/gl"
	"github.com/go-gl/mathgl/mgl32"
)

type Shader struct {
	prog uint32
}


func (s *Shader) Bind() {
	gl.UseProgram(uint32(s.prog))
}

func (s *Shader) UnBind() {
	gl.UseProgram(0)
}

func (s *Shader) getLocation(name string) int32 {
	return gl.GetUniformLocation(s.prog,util.S(&name))
}

func (s *Shader) SetUniform1i(name string, i int32) {
	gl.Uniform1i(s.getLocation(name),i)
}

func (s *Shader) SetUniform1f(name string,f float32) {
	gl.Uniform1f(s.getLocation(name),f)
}

func (s *Shader) SetUnifor3f(name string,v3 mgl32.Vec3) {
	gl.Uniform3f(s.getLocation(name),v3.X(),v3.Y(),v3.Y())
}

func (s *Shader) SetUniform4f(name string,v4 mgl32.Vec4) {
	gl.Uniform4f(s.getLocation(name),v4.X(),v4.Y(),v4.Z(),v4.Z())
}

func (s *Shader) SetUniformMat4f(name string,m4 mgl32.Mat4) {
	gl.UniformMatrix4fv(s.getLocation(name),1,false,&m4[0])
}


// CompileShader 编译shader字符串
func CompileShader(source string,shaderType uint32) (uint32,error) {
	shader := gl.CreateShader(shaderType)
	//Strs是CGO---会返回C语言里面的字符串数组---用完了我们得手动释放内存0，0
	sources,free := gl.Strs(source+"\x00")
	gl.ShaderSource(shader,1,sources,nil)
	free()
	gl.CompileShader(shader)

	var status int32
	gl.GetShaderiv(shader,gl.COMPILE_STATUS,&status)
	if status == gl.FALSE {
		var logLength int32
		gl.GetShaderiv(shader,gl.INFO_LOG_LENGTH,&logLength)

		log := strings.Repeat("\x00",int(logLength+1))
		gl.GetShaderInfoLog(shader,logLength,nil,gl.Str(log))

		var sss string
		if shaderType == gl.VERTEX_SHADER {
			sss = "VERTEX_SHADER"
		} else {
			sss = "FRAGMENT_SHADER"
		}
		return 0,fmt.Errorf("failed to compile %v:%v",sss,log)
	}
	return shader,nil
}


//initShader 创建shader程序
func InitShader(filePath string) (*Shader,error) {
	vertex,fragment,err := loadFileFromShader(filePath)
	if err != nil {
		return nil,err
	}
	vertexShader,err := CompileShader(vertex,gl.VERTEX_SHADER)
	if err != nil {
		return nil,err
	}
	fragmentShader,err := CompileShader(fragment,gl.FRAGMENT_SHADER)
	if err != nil {
		return nil,err
	}
	prog := gl.CreateProgram()
	gl.AttachShader(prog,vertexShader)
	gl.AttachShader(prog,fragmentShader)
	gl.LinkProgram(prog)
	return &Shader{prog: prog},err
}

// loadFileFromShader 从文件中读取shader程序
func loadFileFromShader(filePath string)(string,string,error) {
	f,err := ioutil.ReadFile(filePath)
	if err != nil {
		return "","",err
	}
	var ss [2]string
	//先把byte转化为runes
	var source = bytes.Runes(f)
	const VV = "#VERTEX_SHADER"
	const FF = "#FRAGMENT_SHADER"
	nowType := -1
	index := 0
	for {
		tem := getLine(source,&index)
		stem := string(tem)

		if index == len(source) {
			break
		}

		if strings.Compare(stem,VV)==0 {
			nowType = 0
			continue
		} else if strings.Compare(stem,FF)==0 {
			nowType = 1
			continue
		}

		if nowType == -1 || len(stem)==0 {
			continue
		}

		ss[nowType] = ss[nowType] + stem + "\n"
	}

	return ss[0],ss[1],nil
}

func getLine(source []rune,index *int) ([]rune) {
	var tem = make([]rune,0)

	for ;*index<len(source);*index++ {
		tem = append(tem, source[*index])
		if source[*index] == rune('\n') {
			tem = tem[:len(tem)-2]
			*index++
			break
		}
	}
	return tem
}